package com.millstein.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.millstein.constant.RedisKey;
import com.millstein.context.UserAuthorization;
import com.millstein.enums.BizCodeEnum;
import com.millstein.exception.BizException;
import com.millstein.model.LoginUser;
import com.millstein.model.ProductDO;
import com.millstein.request.CartItemRequest;
import com.millstein.service.CartService;
import com.millstein.service.ProductService;
import com.millstein.vo.CartItemVO;
import com.millstein.vo.CartVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @description:
 * @author: tsing
 * @create: 2021-09-16 22:44
 */
@Service
@Slf4j
public class CartServiceImpl implements CartService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ProductService productService;

    /**
     * 添加购物车。购物车的存储类型为
     *      Map<String, Map<String, Json>>
     * 即
     *
     * @param cartItemRequest 添加购物车所需要的数据
     */
    @Override
    public void addCartItem(CartItemRequest cartItemRequest) {
        LoginUser loginUser = UserAuthorization.getUserContext();
        String redisKey = String.format(RedisKey.CART_KEY, loginUser.getId());
        Map entries = redisTemplate.opsForHash().entries(redisKey);

        String cartItemJson = (String) entries.get(String.valueOf(cartItemRequest.getProductId()));
        CartItemVO cartItemVO = null;
        if (StrUtil.isBlank(cartItemJson)) {
            // 首次添加，需要添加商品信息
            ProductDO productDO = productService.getById(cartItemRequest.getProductId());
            if (productDO == null) {
                throw new BizException(BizCodeEnum.RETURN_DATA_IS_NONE);
            }
            cartItemVO = new CartItemVO();
            cartItemVO.setId(productDO.getId());
            cartItemVO.setBuyNum(cartItemRequest.getBuyNum());
            cartItemVO.setPrice(productDO.getPrice());
            cartItemVO.setProductImage(productDO.getCoverImg());
            cartItemVO.setProductTitle(productDO.getTitle());
        } else {
            // 非首次添加，只需要修改数量
            cartItemVO = JSON.parseObject(cartItemJson, CartItemVO.class);
            cartItemVO.setBuyNum(cartItemVO.getBuyNum() + cartItemRequest.getBuyNum());
        }
        entries.put(String.valueOf(cartItemRequest.getProductId()), JSON.toJSONString(cartItemVO));
        redisTemplate.opsForHash().putAll(redisKey, entries);
    }

    /**
     * 清空购物车
     */
    @Override
    public void clearCart() {
        LoginUser loginUser = UserAuthorization.getUserContext();
        String redisKey = String.format(RedisKey.CART_KEY, loginUser.getId());
        redisTemplate.delete(redisKey);
    }

    /**
     * 删除购物项
     *
     * @param productId 商品id
     */
    @Override
    public void deleteCartItem(Long productId) {
        LoginUser loginUser = UserAuthorization.getUserContext();
        String redisKey = String.format(RedisKey.CART_KEY, loginUser.getId());
        Map entries = redisTemplate.opsForHash().entries(redisKey);

        String cartItemJson = (String) entries.get(String.valueOf(productId));
        if (StrUtil.isBlank(cartItemJson)) {
            return;
        }
        redisTemplate.opsForHash().delete(redisKey, String.valueOf(productId));
    }

    /**
     * 修改购物项数量
     * 注意，这里的数量就是最终要修改的数量，不是改动的数量
     * @param cartItemRequest 修改购物项所需要的数据
     */
    @Override
    public void changeCartItemNumber(CartItemRequest cartItemRequest) {
        LoginUser loginUser = UserAuthorization.getUserContext();
        String redisKey = String.format(RedisKey.CART_KEY, loginUser.getId());
        Map entries = redisTemplate.opsForHash().entries(redisKey);

        String cartItemJson = (String) entries.get(String.valueOf(cartItemRequest.getProductId()));
        if (StrUtil.isBlank(cartItemJson)) {
            throw new BizException(BizCodeEnum.RETURN_DATA_IS_NONE);
        }

        CartItemVO cartItemVO = JSONObject.parseObject(cartItemJson, CartItemVO.class);
        cartItemVO.setBuyNum(cartItemRequest.getBuyNum());
        entries.put(String.valueOf(cartItemRequest.getProductId()), JSON.toJSONString(cartItemVO));
        redisTemplate.opsForHash().putAll(redisKey, entries);
    }

    /**
     * 获取当前用户的购物车
     * 当查询用户的购物车时，我们只展示给用户原来放到购物车中的价格；而在生成订单的时候，我们需要去查询真实的价格
     * @return
     */
    @Override
    public CartVO getCurrentCart() {
        LoginUser loginUser = UserAuthorization.getUserContext();
        CartVO cartVO = new CartVO();
        List<CartItemVO> cartItemVOS = getCartItemsFromRedis(loginUser.getId(), false);

        cartVO.setCartItemList(cartItemVOS);
        return cartVO;
    }

    /**
     * 从购物车中获取数据
     *
     * @param productIds 生成订单商品id的集合
     * @return
     */
    @Override
    public CartVO getProductFromCart(List<Long> productIds) {
        LoginUser loginUser = UserAuthorization.getUserContext();
        CartVO cartVO = new CartVO();
        // 获取真实的商品信息（主要是价格可能会变动）
        List<CartItemVO> cartItemVOS = getCartItemsFromRedis(loginUser.getId(), true);

        // 筛选出下单的商品
        cartItemVOS = cartItemVOS.stream().filter(item -> productIds.contains(item.getId())).collect(Collectors.toList());
        cartVO.setCartItemList(cartItemVOS);

        // 从购物车中删掉已经下单的商品
        for (Long id : productIds) {
            this.deleteCartItem(id);
        }
        return cartVO;
    }

    /**
     * 从redis中拿购物项信息
     * @param userId 用户id
     * @param getRealPrice 是否需要查询真实的价格
     * @return
     */
    private List<CartItemVO> getCartItemsFromRedis(Long userId, boolean getRealPrice) {
        String redisKey = String.format(RedisKey.CART_KEY, userId);
        Map entries = redisTemplate.opsForHash().entries(redisKey);
        List<CartItemVO> list = new ArrayList<>();
        if (!entries.isEmpty()) {
            entries.forEach((key, value) -> {
                String CartItemJson = (String) entries.get(key);
                list.add(JSONObject.parseObject(CartItemJson, CartItemVO.class));
            });
        }
        if (getRealPrice) {
            setRealPrice(list);
        }
        return list;
    }

    /**
     * 查询真实价格，并赋值给购物项中
     * @param list 购物项列表
     */
    private void setRealPrice(List<CartItemVO> list) {
        List<Long> productIds = list.stream().map(CartItemVO::getId).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(productIds)) {
            return;
        }
        List<ProductDO> productDOS = productService.listByIds(productIds);
        if (productDOS.size() != productIds.size()) {
            throw new BizException(BizCodeEnum.RETURN_DATA_IS_NONE);
        }
        Map<Long, ProductDO> collect = productDOS.stream().collect(Collectors.toMap(ProductDO::getId, Function.identity()));
        list.forEach(item -> item.setPrice(collect.get(item.getId()).getPrice()));
    }
}
